﻿using System;
using System.Collections.Generic;
using System.Linq;

using RayDen.Library.Core.Collections;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.IntersectionEngines;

namespace PathEngine
{
    public class TriangleMesh : Shape
    {
        public static int delta = 0;

        protected Point[]   Vertices;
        protected Normal[]  Normals;
        protected Vector[]  TexCoords;
        protected TriangleDataInfo[] Triangles;
        public AccellerationData BottomLevelBVH;


        public string MeshName { get; set; }
        public string MaterialName { get; set; }
        public int StartTriangle { get; set; }
        public int EndTriangle { get; set; }
        public int StartVertice, VerticesOffset;
        public int EndVertice;
        public float Area;
        public GeometryProfile MeshProfile { get; set; }
        public bool HasNormals { get; set; }
        public bool HasTexCoords { get; set; }

        public TriangleMesh(GeometryInfo geoInfo)
        {
            MeshProfile = new SurfaceProfile();
            MaterialName = geoInfo.MaterialName;
            MeshName = geoInfo.Name;
            Vertices = geoInfo.VertexData.ToPointList().ToArray();
            Normals = geoInfo.NormalData.ToNormalList().ToArray();
            TexCoords = geoInfo.TextureData.TodVectorList().ToArray();
            var triangles = new List<TriangleDataInfo>();
            int index = 0;
            if (geoInfo.IndexData.Any())
            {
                int[] ni = null;

                int[] ti = null;
                var hasNormals = geoInfo.NormalIndexData != null && geoInfo.NormalIndexData.Any();
                var hasTexCoords = geoInfo.TextureIndexData != null && geoInfo.TextureIndexData.Any();
                if (hasTexCoords)
                {
                     ti = geoInfo.TextureIndexData.ToArray();
                }
                if (hasNormals)
                {
                     ni = geoInfo.NormalIndexData.ToArray();
                }
                HasNormals = hasNormals;
                HasTexCoords = hasTexCoords;
                for (int i = 0; i < geoInfo.IndexData.Count; i += 3)
                {
                    index++;
                    var newTriangle = new TriangleDataInfo()
                    {
                        v0 = new MeshVertexInfo()
                        {
                            NormalIndex = hasNormals ? ni[i] - 1 : -1,
                            VertexIndex = geoInfo.IndexData[i] - delta,
                            TexCoordIndex = hasTexCoords ? ti[i] - 1 : -1
                        },
                        v1 = new MeshVertexInfo()
                        {
                            NormalIndex = hasNormals ? ni[i + 1] - 1 : -1,
                            VertexIndex = geoInfo.IndexData[i + 1] - delta,
                            TexCoordIndex = hasTexCoords ? ti[i + 1] - 1 : -1
                        },
                        v2 = new MeshVertexInfo()
                        {
                            NormalIndex = hasNormals ? ni[i + 2] - 1 : -1,
                            VertexIndex = geoInfo.IndexData[i + 2] - delta,
                            TexCoordIndex = hasTexCoords ? ti[i + 2] - 1 : -1
                        },

                    };
                    triangles.Add(newTriangle);
                }
                WorldBound = new AABB(this.Vertices);
                this.Triangles = triangles.ToArray();
                this.BottomLevelBVH = new AccellerationData() {data = BvhDataAdapter.CreateBVH(Triangles, Vertices)};
            }
        }

        public bool TriangleInRange(int triIndex)
        {
            var start = triIndex != 0 ? StartTriangle + delta : triIndex;
            var end = Math.Max(0, EndTriangle - delta);

            return (triIndex <= (end) && triIndex >= start);
        }

        public void InterpolateTriUV(int currentTriangleIndex, float b1, float b2, out UV texCoord)
        {
            var texCoords = TexCoords;
            if (TexCoords == null || Triangles[currentTriangleIndex].v0.TexCoordIndex < 0)
                texCoord = new UV { U = b1, V = b2 };
            else
            {
                var t0 = texCoords[Triangles[currentTriangleIndex].v0.TexCoordIndex];
                var t1 = texCoords[Triangles[currentTriangleIndex].v1.TexCoordIndex];
                var t2 = texCoords[Triangles[currentTriangleIndex].v2.TexCoordIndex];
                float b0 = 1f - b1 - b2;

                //            return t0 * b1 + t1 * b2 + t2 * b0;
                texCoord = new UV(t0.x * b0 + t1.x * b1 + t2.x * b2, t0.y * b0 + t1.y * b1 + t2.y * b2);
            }
        }

        public void InterpolateTriangleNormal(int currentTriangleIndex, float b1, float b2, ref Normal Normal)
        {
            var normals = Normals;
            var n0 = normals[Triangles[currentTriangleIndex].v0.NormalIndex];
            var n1 = normals[Triangles[currentTriangleIndex].v1.NormalIndex];
            var n2 = normals[Triangles[currentTriangleIndex].v2.NormalIndex];

            float b0 = 1f - b1 - b2;
            Normal = new Normal(b0 * n0.x + b1 * n1.x + b2 * n2.x,
                                b0 * n0.y + b1 * n1.y + b2 * n2.y,
                                b0 * n0.z + b1 * n1.z + b2 * n2.z).Normalize();
            //return b0 * n0 + b1 * n1 + b2 * n2;
        }

        public int TrianglesCount { get { return EndTriangle - StartTriangle; } }
    }
}